package com.flesler.stickjoy.mapper
{
	import com.flesler.stickjoy.controller.Controller;
	import com.flesler.stickjoy.controller.ControllerAxis;
	import com.flesler.stickjoy.controller.ControllerButton;
	import com.flesler.stickjoy.controller.ControllerComponent;
	import com.flesler.stickjoy.net.reader.InputReader;
	import com.flesler.stickjoy.net.reader.InputReaderEvent;
	import com.flesler.stickjoy.net.token.ComponentToken;
	import com.flesler.stickjoy.net.token.ControllerToken;
	import com.flesler.stickjoy.net.token.value.AnalogComponentValue;
	import com.flesler.stickjoy.net.token.value.ComponentValue;
	import com.flesler.stickjoy.net.token.value.DigitalComponentValue;
	
	import flash.events.EventDispatcher;
	import flash.utils.IDataInput;

	public final class ControllerInputMapper extends EventDispatcher
	{
		private var input:InputReader;
		private var map:Object = {};
		
		public function ControllerInputMapper(stream:IDataInput)
		{
			this.input = new InputReader(stream);
			
			init();
		}
		
		private function init():void
		{
			input.addEventListener(InputReaderEvent.INPUT, parseInput);
		}
		
		public function read():void
		{
			input.read();
		}
		
		private function parseInput(e:InputReaderEvent):void
		{
			var token:ControllerToken = e.token;
			if (getController(token.key))
				update(token);
			else
				create(token);
		}
		
		private function create(token:ControllerToken):void
		{
			var controller:Controller = new Controller(token.key);
			for each (var componentToken:ComponentToken in token.components)
				updateComponent(controller, componentToken);
			map[controller.key] = controller;
			event(ControllerInputMapperEvent.ADDED, controller);
		}
		
		private function update(token:ControllerToken):void
		{
			var key:String = token.key;
			var controller:Controller = getController(key);
			var components:Array = token.components;
			
			if (components)
			{
				for each (var componentToken:ComponentToken in components)
					updateComponent(controller, componentToken);
			}
			// Will be null when removing
			else
			{
				event(ControllerInputMapperEvent.REMOVED, controller);
				controller.destroy();
				delete map[key];
			}
			
		}
		
		private function updateComponent(controller:Controller, token:ComponentToken):void
		{
			// OH NO! no polymorphism!
			// TODO: Refactor please
			var key:String = token.key;
			var value:ComponentValue = token.value;
			var component:ControllerComponent = controller.getComponent(key);
			if (!component)
			{
				component = value.isAnalog ? new ControllerAxis(key) : new ControllerButton(key);
				controller.add(component);
			}
			
			if (value.isAnalog)
				ControllerAxis(component).position = AnalogComponentValue(value).value;
			else
				ControllerButton(component).isDown = DigitalComponentValue(value).state;
		}
		
		private function event(type:String, controller:Controller):void
		{
			dispatchEvent(new ControllerInputMapperEvent(type, controller));
		}
		
		private function getController(key:String):Controller
		{
			return map[key];
		}
		
		// Destroying
		
		public function destroy():void
		{
			for each (var controller:Controller in map)
				controller.destroy();
			map = null;
			
			input.removeEventListener(InputReaderEvent.INPUT, parseInput);
			input = null;
		}
	}
}